package com.hup.sokoban.view.main;

import com.hup.sokoban.model.common.MapItem;
import com.hup.sokoban.model.main.MainConfig;
import com.hup.sokoban.model.main.MapInfo;
import com.hup.sokoban.mvp.MainMvp.MainPresenter;
import com.hup.sokoban.service.common.AudioService;
import com.hup.sokoban.service.common.AutoplayWinningService;
import com.hup.sokoban.service.common.AutoplayWinningService.AutoplayObserver;
import com.hup.sokoban.service.common.AutoplayWinningService.AutoplayPlayer;
import com.hup.sokoban.service.common.PlayerKeyService;
import com.hup.sokoban.service.common.PlayerKeyService.PlayerKeyHandler;
import com.hup.sokoban.service.main.LabelMouseGesture;
import com.hup.sokoban.view.customView.MapLabel;
import com.hup.sokoban.view.customView.StepRecordPanel;
import com.hup.sokoban.view.mapDesigner.MapDesignerFrame;
import com.hup.sokoban.view.winning.WinningDialog;
import com.hup.utils.commons.ListUtil;
import com.hup.utils.commons.hardware.ScreenUtil;
import com.hup.utils.commons.hardware.SystemUtil;
import com.hup.utils.commons.start.DialogHelper;
import com.hup.utils.commons.start.SimpleExitHandler;
import com.hup.utils.commons.start.SpringContext;
import com.hup.utils.swing.frame.BaseFrame;
import com.hup.utils.swing.frame.HelpDialog;
import com.hup.utils.swing.frame.WindowConfigBinder;
import com.hup.utils.swing.frame.WindowConfigBinder.FrameStateHandler;
import com.hup.utils.swing.model.BaseWindowConfig;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * @author hugan
 * @date 2020/4/28
 */
@Log4j2
public class MainFrame extends BaseFrame implements AutoplayObserver {

    private final MainPresenter presenter;
    private final AudioService audioService;
    private final MainConfig config;
    private MainForm form;

    /**
     * 当前被加载的关卡信息,notNull
     */
    private MapInfo curMapInfo;
    @Setter
    private AutoplayWinningService autoplayWinningService;

    public MainFrame() {
        super("推箱子");
        presenter = SpringContext.getBean(MainPresenter.class);
        audioService = SpringContext.getBean(AudioService.class);
        config = presenter.getConfig();

        initView();
        initListener();
        initConfig();
    }

    private void initConfig() {
        config.windowConfig.initFrame(this);
        audioService.setMute(config.mute);
        form.btnMute.setSelected(config.mute);
    }

    private void initView() {
        form = new MainForm();
        setContentPane(form.root);

        Rectangle usableBounds = ScreenUtil.getScreenUsableBounds();
        log.debug("usableBounds={}", usableBounds);
        setSize(usableBounds.width / 2, usableBounds.height / 2);
        setLocationRelativeTo(null);
        SystemUtil.disableInputMethods(this, true);

        form.pLeft.setVisible(false);
        form.btnReset.setEnabled(false);
        form.btnSaveProgress.setEnabled(false);
        form.btnLoadProgress.setVisible(false);
        form.btnDeleteProgress.setVisible(false);
        form.btnLoadWinning.setVisible(false);
        form.btnDeleteWinning.setVisible(false);

        if (audioService.getLoadErr() != null) {
            form.btnMute.setSelected(true);
            form.btnMute.setEnabled(false);
            StringBuilder sb = new StringBuilder("<html>静音");
            for (String err : audioService.getLoadErr()) {
                sb.append("<br>").append(err);
            }
            form.btnMute.setToolTipText(sb.toString());
        }
    }

    private void initListener() {
        WindowConfigBinder.bind(this, new FrameStateHandler() {
            @Override
            public BaseWindowConfig getConfig() {
                return config.windowConfig;
            }

            @Override
            public void saveConfig(String desc) {
                log.debug("desc={}", desc);
                config.save();
            }
        });

        form.mapListView.setMenuListener(mapItem -> {
            boolean canLoad = true;
            if (hadChanged()) {
                canLoad = DialogHelper.yesNo("进度未保存,确认加载关卡?").show(MainFrame.this);
            }
            if (canLoad) {
                execute("加载关卡", () -> presenter.getMapInfo(mapItem), this::loadMap);
            }
            return canLoad;
        });

        LabelMouseGesture.bind(this, form);
        PlayerKeyService.bind(new PlayerKeyHandler() {
            @Override
            public MapLabel getMapLabel() {
                return form.mapLabel;
            }

            @Override
            public StepRecordPanel getStepRecordPanel() {
                return form.stepRecordPanel;
            }

            @Override
            public boolean canMove() {
                return !autoplayWinningService.isEnabled();
            }

            @Override
            public void afterMoved() {
                form.btnReset.setEnabled(true);
                form.btnSaveProgress.setEnabled(true);
            }

            @Override
            public void beforeUndoRedo(boolean isUndo) {
                autoplayWinningService.setPlayState(false);
            }

            @Override
            public void onWinning() {
                MainFrame.this.onWinning();
            }
        });
        initAutoplayListener();

        initBottomListener();
    }

    private void initAutoplayListener() {
        //自动播放记录
        AutoplayWinningService.bind(new AutoplayPlayer() {
            @Override
            public int beforeStart() {
                return curMapInfo.lstWinningRecord.size();
            }

            @Override
            public boolean playNext() {
                return !form.stepRecordPanel.redo();
            }
        }, this, form.stepRecordPanel);
        //空格控制播放,原有的CTRL+Z/Y控制前进后退
        form.mapLabel.registerKeyboardAction(e -> {
            if (autoplayWinningService.isEnabled()) {
                autoplayWinningService.setPlayState(!autoplayWinningService.isPlaying());
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
    }

    private void initBottomListener() {
        form.cbMapList.addActionListener(e -> form.pLeft.setVisible(form.cbMapList.isSelected()));
        form.cbMapList.registerKeyboardAction(e -> ((AbstractButton) e.getSource()).doClick(),
                KeyStroke.getKeyStroke(KeyEvent.VK_X, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);

        form.btnReset.addActionListener(confirm("重玩", () -> {
            form.mapLabel.setData(curMapInfo.mapItem.getClone());
            form.mapLabel.requestFocus();
            form.stepRecordPanel.reset();
            form.btnReset.setEnabled(false);
            form.btnSaveProgress.setEnabled(false);
            autoplayWinningService.setEnable(false);
        }));
        form.btnSaveProgress.addActionListener(e -> {
            if (curMapInfo.mapItem.getPath() == null) {
                DialogHelper.confirm("没有关卡啊").show(this);
                return;
            }
            if (autoplayWinningService.isEnabled()) {
                //开启自动播放后,保存通关记录的片段
                curMapInfo.lstProgressRecord = form.stepRecordPanel.getStepClone(true);
            } else {
                curMapInfo.lstProgressRecord = form.stepRecordPanel.getStepClone(false);
            }
            presenter.saveProgress(curMapInfo);
            form.btnLoadProgress.setVisible(true);
            form.btnDeleteProgress.setVisible(true);
            curMapInfo.mapItem.refreshRecordsState();
            form.mapListView.repaint();
            form.mapLabel.requestFocus();
        });
        form.btnLoadProgress.setActionListener(confirm("加载进度", () -> {
            form.mapLabel.setData(curMapInfo.mapItem.getClone());
            form.stepRecordPanel.loadStep(curMapInfo.lstProgressRecord);
            form.stepRecordPanel.toLastStep();
            form.btnReset.setEnabled(true);
            autoplayWinningService.setEnable(false);
            form.mapLabel.requestFocus();
        }));
        form.btnDeleteProgress.setActionListener(confirm("删除进度", () -> {
            presenter.deleteProgress(curMapInfo);
            form.btnLoadProgress.setVisible(false);
            form.btnDeleteProgress.setVisible(false);
            curMapInfo.mapItem.refreshRecordsState();
            form.mapListView.repaint();
            form.mapLabel.requestFocus();
        }));
        form.btnLoadWinning.addActionListener(confirm("加载通关记录", () -> {
            form.mapLabel.setData(curMapInfo.mapItem.getClone());
            form.mapLabel.requestFocus();
            form.stepRecordPanel.loadStep(curMapInfo.lstWinningRecord);
            form.btnReset.setEnabled(true);
            form.btnSaveProgress.setEnabled(true);//允许保存通关记录片段
            form.btnNextMap.setVisible(true);
            autoplayWinningService.setEnable(true);
            autoplayWinningService.setPlayState(true);
            form.mapLabel.requestFocus();
        }));
        form.btnDeleteWinning.setActionListener(confirm("删除通关记录", () -> {
            presenter.deleteWinning(curMapInfo);
            form.btnLoadWinning.setVisible(false);
            form.btnDeleteWinning.setVisible(false);
            curMapInfo.mapItem.refreshRecordsState();
            form.mapListView.repaint();
            autoplayWinningService.setEnable(false);
            form.mapLabel.requestFocus();
        }));
        ActionListener acLoadNextMap = e -> {
            boolean canLoad = true;
            if (hadChanged()) canLoad = DialogHelper.yesNo("进度未保存,确认加载关卡?").show(MainFrame.this);
            if (canLoad) loadNextMap(false);
        };
        form.btnNextMap.setActionListener(acLoadNextMap);
        form.btnNextMap.registerKeyboardAction(acLoadNextMap,
                KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);

        form.btnMapDesigner.addActionListener(e -> {
            boolean ok = true;
            if (hadChanged()) ok = DialogHelper.yesNo("确认[丢弃游戏记录]?").show(this);
            if (ok) MapDesignerFrame.showFrame(this, config.windowConfig);
        });
        form.btnHelp.setActionListener(e -> HelpDialog.builder().model(false).doShow(this, "MainFrame"));
        form.btnMute.setActionListener(e -> {
            boolean mute = form.btnMute.isSelected();
            audioService.setMute(mute);
            config.mute = mute;
            config.save();
            form.mapLabel.requestFocus();
        });
    }

    @Override
    public void onShown() {
        log.info("");
        execute("加载关卡", new ExecuteTask<MapInfo>() {
            ArrayList<MapItem> mapItems;

            /**
             * 加载关卡列表
             * @return 返回要加载的关卡信息;nullable-没有关卡
             */
            @Override
            public MapInfo call() throws Exception {
                mapItems = presenter.loadMapList();
                if (mapItems.isEmpty()) return presenter.getEmptyMapInfo();

                MapItem lastLoadedMap = ListUtil.get(mapItems, mapItem -> mapItem.getPath().equals(config.lastLoadedMapPath));
                if (lastLoadedMap == null) {
                    log.info("找不到历史加载记录={}", config.lastLoadedMapPath);
                    //没有历史加载记录,或者记录不匹配(删除了文件)
                    lastLoadedMap = mapItems.get(0);//就加载第一项
                }
                return presenter.getMapInfo(lastLoadedMap);
            }

            @Override
            public void accept(MapInfo mapInfo) {
                form.mapListView.setMapItems(mapItems);
                loadMap(mapInfo);
            }
        });
    }

    @Override
    public boolean onWindowClosing(WindowEvent e) {
        boolean exit = true;
        if (hadChanged()) {
            exit = DialogHelper.yesNo("进度未保存,确认退出程序?").show(this);
        }
        log.debug("exit={}", exit);
        return exit;
    }

    @Override
    public void onHidden(boolean dispose) {
        log.info("dispose={}", dispose);
        if (dispose) {
            SpringContext.getBean(SimpleExitHandler.class).doExit();
        }
    }

    /**
     * 加载关卡;
     * <pre>
     * 加载逻辑:
     * .已通关,加载原始地图
     * .有历史,加载历史
     * </pre>
     */
    private void loadMap(MapInfo mapInfo) {
        autoplayWinningService.setEnable(false);

        curMapInfo = mapInfo;
        MapItem mapItem = mapInfo.mapItem;

        setSubTitle(mapItem.getName());
        form.mapListView.setSelectedItem(mapItem);
        //游戏中,修改的是克隆对象的数据
        form.mapLabel.setData(mapItem.getClone());
        form.mapLabel.requestFocus();
        form.stepRecordPanel.reset();

        form.btnReset.setEnabled(false);

        boolean hadPlaying = curMapInfo.lstProgressRecord != null;
        form.btnSaveProgress.setEnabled(false);
        form.btnLoadProgress.setVisible(hadPlaying);
        form.btnDeleteProgress.setVisible(hadPlaying);

        boolean hadWinning = curMapInfo.lstWinningRecord != null;
        form.btnLoadWinning.setVisible(hadWinning);
        form.btnDeleteWinning.setVisible(hadWinning);

        if (!hadWinning && hadPlaying) {
            //未通关,且有历史:才加载历史
            form.stepRecordPanel.loadStep(curMapInfo.lstProgressRecord);
            form.stepRecordPanel.toLastStep();
            form.btnReset.setEnabled(true);
        }

        form.btnNextMap.setVisible(mapItem.isWinning());

        //加载成功后保存配置
        config.lastLoadedMapPath = mapItem.getPath();
        config.save();
        log.info("name={}", mapItem.getName());
    }

    /**
     * 有无修改过游戏内容
     */
    private boolean hadChanged() {
        if (curMapInfo == null) return false;//当前数据未异步加载完
        boolean hasRecord = form.stepRecordPanel.hasRecord();
        boolean sameWithProgress = form.stepRecordPanel.isSameWithStep(curMapInfo.lstProgressRecord);
        boolean sameWithWinning = form.stepRecordPanel.isSameWithStep(curMapInfo.lstWinningRecord);
        log.debug("hasRecord={}, sameWithProgress={} sameWithWinning={}", hasRecord, sameWithProgress, sameWithWinning);
        return !(!hasRecord || sameWithProgress || sameWithWinning);
    }

    private void onWinning() {
        execute("保存通关记录", (Callable<Void>) () -> {
            log.info("保存通关记录");
            curMapInfo.lstWinningRecord = form.stepRecordPanel.getStepClone(false);
            presenter.saveWinning(curMapInfo);
            return null;
        }, aVoid -> {
            form.btnLoadWinning.setVisible(true);
            form.btnDeleteWinning.setVisible(true);
            curMapInfo.mapItem.refreshRecordsState();
            form.mapListView.repaint();
            autoplayWinningService.setEnable(true);
            form.btnNextMap.setVisible(true);

            WinningDialog.showFrame(this);
        });
    }

    /**
     * 加载下一关
     */
    private void loadNextMap(boolean afterWinning) {
        execute("加载下一关", () -> {
            MapItem nextMap = null;
            List<MapItem> mapItems = form.mapListView.getMapItems();
            int curIndex = mapItems.indexOf(curMapInfo.mapItem);
            if (!afterWinning) {
                //本局是已过关的(用户点了下一局),找下一局;
                nextMap = mapItems.get((curIndex + 1) % mapItems.size());
            } else if (mapItems.size() > 1) {
                //否则,是用户通关了: 找下一个未通关的局
                //查找范围: 当前局+1 -> size -> 当期局-1
                int endIndex = curIndex == -1 ? mapItems.size() : mapItems.size() + curIndex;
                for (int i = curIndex + 1; i < endIndex; i++) {
                    int nextIndex = (i % mapItems.size());
                    //log.info("curIndex={}, endIndex={}, nextIndex={}", curIndex, endIndex, nextIndex);
                    MapItem mapItem = mapItems.get(nextIndex);
                    mapItem.refreshRecordsState();
                    if (!mapItem.isWinning()) {
                        nextMap = mapItem;
                        break;
                    }
                }
            }
            //都通关,就加载下一局
            if (nextMap == null) nextMap = mapItems.get((curIndex + 1) % mapItems.size());
            return presenter.getMapInfo(nextMap);
        }, mapInfo -> {
            if (afterWinning && mapInfo.mapItem.isWinning()) {
                DialogHelper.confirm("所有关卡都通关了!").show(this);
            }
            loadMap(mapInfo);
        });
    }

    @Override
    public void onAutoplayEnabled(boolean enabled) {
        form.mapLabel.setReadOnly(enabled);
        form.mapLabel.repaint();
    }

    /**
     * {@link WinningDialog}点击了[下一关]
     */
    public void wonAndToNextMap() {
        loadNextMap(true);
    }

}
